package com.um.jdy.tcp.event;


import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.um.jdy.rpc.enums.*;
import com.um.jdy.tcp.base.Socket;
import com.um.jdy.tcp.cmd.CommandSender;
import com.um.jdy.tcp.downlink.Cmd32ExceptionResp;
import com.um.jdy.tcp.downlink.Cmd33RemoveExceptionResp;
import com.um.jdy.tcp.uplink.*;
import com.um.jdy.rpc.entity.EBDeviceInfo;
import com.um.jdy.rpc.entity.EBPort;
import com.um.jdy.common.device.enums.DeviceEnum;
import com.um.jdy.rpc.notify.RpcEBDeviceNotifyService;
import com.um.jdy.rpc.notify.RpcEBOrderNotifyService;
import com.um.springboot.starter.utils.StringUtil;
import com.um.springcloud.dubbo.cluster.DeviceClusterUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.common.utils.NetUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import com.um.jdy.common.charge.enums.OrderStopReason;

import java.util.ArrayList;
import java.util.List;

import static jdk.nashorn.internal.runtime.ECMAException.getException;

/**
 * 事件处理监听器
 *
 * @author 甘天伟
 * @date 2022/1/28
 * 版权所有 广州优匠科技有限公司
 */
@Slf4j
@Component
public class AppEventListener {

    @DubboReference
    RpcEBDeviceNotifyService rpcEBDeviceNotifyService;

    @DubboReference
    RpcEBOrderNotifyService rpcEBOrderNotifyService;

    @Autowired
    CommandSender commandSender;

    @Autowired
    DeviceClusterUtil deviceClusterUtil;

    @Async
    @EventListener
    public void handleLoginReqEvent(AppEvent<Cmd00LoginReq> event) {
        try {
            log.info("handle device {} event {} data {}", event.getDeviceName(), event.getData().getClass().getSimpleName(),
                    JSONUtil.toJsonStr(event.getData()));

            // 设置设备的服务为当前实例IP
            deviceClusterUtil.setDeviceEndpoint(event.getDeviceName(), NetUtils.getLocalHost());

            Cmd00LoginReq req = event.getData();
            EBDeviceInfo deviceInfo = new EBDeviceInfo();
            deviceInfo.setHeart(req.getHeart());
            deviceInfo.setIccid(req.getIccid());
            deviceInfo.setRssi(req.getRssi());
            deviceInfo.setType(DeviceTypeEnum.EB.name());
            deviceInfo.setProtocol(DeviceProtocolEnum.XH_TCP_10.getKey());
            deviceInfo.setVersion(req.getVersion());
            deviceInfo.setEbType(req.getDeviceType() == 0 ? WireEnum.Wired.name() : WireEnum.Wireless.name());
            String ipAddress = NetUtils.getLocalHost(); //当前中央服务实例IP
            deviceInfo.setIpAddress(ipAddress);
            rpcEBDeviceNotifyService.reqDeviceLogin(JSONUtil.toJsonStr(req),req.getSeq(),req.getDevice(),toPorts(req.getAllData()),deviceInfo);
        } catch (Exception e) {
            //TODO:重试机制处理
            log.error("handle event error ", e);
        }
    }

    @Async
    @EventListener
    public void handleHeartReqEvent(AppEvent<Cmd01HeartReq> event) {
        try {
            log.info("handle device {} event {} data {}", event.getDeviceName(), event.getData().getClass().getSimpleName(),
                    JSONUtil.toJsonStr(event.getData()));

            Cmd01HeartReq req = event.getData();
            EBDeviceInfo deviceInfo = new EBDeviceInfo();
            deviceInfo.setRssi(req.getRssi());
            deviceInfo.setTemp(req.getTemp());
            deviceInfo.setProtocol(DeviceProtocolEnum.XH_TCP_10.getKey());
            rpcEBDeviceNotifyService.reqDeviceHeart(JSONUtil.toJsonStr(req),req.getSeq(),req.getDevice(),toPorts(req.getAllData()),deviceInfo);
        } catch (Exception e) {
            //TODO:重试机制处理
            log.error("handle event error ", e);
        }
    }

    @Async
    @EventListener
    public void handleStartChargeRespEvent(AppEvent<Cmd03StartChargeResp> event) {
        try {
            log.info("handle device {} event {} data {}", event.getDeviceName(), event.getData().getClass().getSimpleName(),
                    JSONUtil.toJsonStr(event.getData()));

            Cmd03StartChargeResp resp = event.getData();
            rpcEBOrderNotifyService.respStartCharge(JSONUtil.toJsonStr(resp),resp.getSeq(),resp.getDevice(),resp.getPort(),toPorts(resp.getAllData()));
        } catch (Exception e) {
            //TODO:重试机制处理
            log.error("handle event error ", e);
        }
    }

    @Async
    @EventListener
    public void handleStopChargeRespEvent(AppEvent<Cmd04StopChargeResp> event) {
        try {
            log.info("handle device {} event {} data {}", event.getDeviceName(), event.getData().getClass().getSimpleName(),
                    JSONUtil.toJsonStr(event.getData()));
            Cmd04StopChargeResp resp = event.getData();
            rpcEBOrderNotifyService.respStopCharge(JSONUtil.toJsonStr(resp),resp.getSeq(),resp.getDevice(),resp.getPort(),toPorts(resp.getAllData()));
        } catch (Exception e) {
            //TODO:重试机制处理
            log.error("handle event error ", e);
        }
    }

    @Async
    @EventListener
    public void handleQuerySocketRespEvent(AppEvent<Cmd05QuerySocketResp> event) {
        try {
            log.info("handle device {} event {} data {}", event.getDeviceName(), event.getData().getClass().getSimpleName(),
                    JSONUtil.toJsonStr(event.getData()));

            Cmd05QuerySocketResp resp = event.getData();
            rpcEBDeviceNotifyService.respQueryPorts(JSONUtil.toJsonStr(resp),resp.getSeq(),resp.getDevice(),toPorts(resp.getAllData()));
        } catch (Exception e) {
            //TODO:重试机制处理
            log.error("handle event error ", e);
        }
    }

    @Async
    @EventListener
    public void handleSlotCardReqEvent(AppEvent<Cmd06SlotCardReq> event) {
        try {
            log.info("handle device {} event {} data {}", event.getDeviceName(), event.getData().getClass().getSimpleName(),
                    JSONUtil.toJsonStr(event.getData()));

            Cmd06SlotCardReq req = event.getData();
            rpcEBOrderNotifyService.reqSlotCard(JSONUtil.toJsonStr(req),req.getSeq(),req.getDevice(),DeviceProtocolEnum.XH_TCP_10.getKey(),req.getCardId(),req.getAmount(),req.getPort());
        } catch (Exception e) {
            //TODO:重试机制处理
            log.error("handle event error ", e);
        }
    }

    @Async
    @EventListener
    public void handleInsertCoinReqEvent(AppEvent<Cmd07InsertCoinReq> event) {
        try {
            log.info("handle device {} event {} data {}", event.getDeviceName(), event.getData().getClass().getSimpleName(),
                    JSONUtil.toJsonStr(event.getData()));
            Cmd07InsertCoinReq req = event.getData();
            rpcEBOrderNotifyService.reqInsertCoin(JSONUtil.toJsonStr(req),req.getSeq(),req.getDevice(),DeviceProtocolEnum.XH_TCP_10.getKey(),req.getPort(),req.getCoinNum());
        } catch (Exception e) {
            //TODO:重试机制处理
            log.error("handle event error ", e);
        }
    }

    @Async
    @EventListener
    public void handleRestartRespEvent(AppEvent<Cmd10RestartResp> event) {
        try {
            log.info("handle device {} event {} data {}", event.getDeviceName(), event.getData().getClass().getSimpleName(),
                    JSONUtil.toJsonStr(event.getData()));
            Cmd10RestartResp resp = event.getData();
            rpcEBDeviceNotifyService.respRestart(JSONUtil.toJsonStr(resp),resp.getSeq(),resp.getDevice());
        } catch (Exception e) {
            //TODO:重试机制处理
            log.error("handle event error ", e);
        }
    }

    @Async
    @EventListener
    public void handleIotStatusEvent(AppEvent<DeviceIotEnum> event) {
        try {
            log.info("handle device {} event {} data {}", event.getDeviceName(), event.getData().getClass().getSimpleName(),
                    event.getData());
            rpcEBDeviceNotifyService.reqDeviceIotStatus(event.getDeviceName(), event.getData());
        } catch (Exception e) {
            //TODO:重试机制处理
            log.error("handle event error ", e);
        }
    }

    @Async
    @EventListener
    public void handleSocketNofityReqEvent(AppEvent<Cmd02SocketNofityReq> event) {
        try {
            log.info("handle device {} event {} data {}", event.getDeviceName(), event.getData().getClass().getSimpleName(),
                    JSONUtil.toJsonStr(event.getData()));
            Cmd02SocketNofityReq req = event.getData();
            rpcEBOrderNotifyService.reqPortsNotify(JSONUtil.toJsonStr(req),req.getSeq(),req.getDevice(),DeviceProtocolEnum.XH_TCP_10.getKey(),toPorts(req.getAllData()));
        } catch (Exception e) {
            //TODO:重试机制处理
            log.error("handle event error ", e);
        }
    }

    @Async
    @EventListener
    public void handleTTSRespReqEvent(AppEvent<Cmd09TTSResp> event) {
        try {
            log.info("handle device {} event {} data {}", event.getDeviceName(), event.getData().getClass().getSimpleName(),
                    JSONUtil.toJsonStr(event.getData()));
            Cmd09TTSResp resp = event.getData();
            rpcEBDeviceNotifyService.respPlayTTS(JSONUtil.toJsonStr(resp),resp.getSeq(),resp.getDevice());
        } catch (Exception e) {
            //TODO:重试机制处理
            log.error("handle event error ", e);
        }
    }

    @Async
    @EventListener
    public void handleExceptionReqEvent(AppEvent<Cmd32ExceptionReq> event) {
        try {
            log.debug("handle device {} event {} data {}", event.getDeviceName(), event.getData().getClass().getSimpleName(),
                    JSON.toJSONString(event.getData()));

            Cmd32ExceptionReq req = event.getData();

            int port = req.getPort();
            if(port != 255)

                rpcEBDeviceNotifyService.reqException(req.getDevice(),port,getException(req.getCode()));

            Cmd32ExceptionResp resp = new Cmd32ExceptionResp();
            resp.setSeq(req.getSeq());
            resp.setDevice(req.getDevice());
            commandSender.sendCmd(req.getDevice(),resp);
        } catch (Exception e) {
            //TODO:重试机制处理
            log.error("handle event error ", e);
        }
    }

    @Async
    @EventListener
    public void handleRemoveExceptionReqEvent(AppEvent<Cmd33RemoveExceptionReq> event) {
        try {
            log.debug("handle device {} event {} data {}", event.getDeviceName(), event.getData().getClass().getSimpleName(),
                    JSON.toJSONString(event.getData()));

            Cmd33RemoveExceptionReq req = event.getData();

            int port = req.getPort();
            if(port != 255)

                rpcEBDeviceNotifyService.reqRemoveException(req.getDevice(),port,getException(req.getCode()));

            Cmd33RemoveExceptionResp resp = new Cmd33RemoveExceptionResp();
            resp.setSeq(req.getSeq());
            resp.setDevice(req.getDevice());
            commandSender.sendCmd(req.getDevice(),resp);
        } catch (Exception e) {
            //TODO:重试机制处理
            log.error("handle event error ", e);
        }
    }

    private List<EBPort> toPorts(List<Socket> list){
        List<EBPort> ports = new ArrayList<>();

        if(null == list){
            return ports;
        }

        for(Socket socket : list){
            EBPort port = new EBPort();
            port.setDuration(socket.getOutTime());
            port.setEnergy(StringUtil.toLong(socket.getEnergy()) / 60);
            port.setPort(socket.getPort());
            port.setPower(socket.getPower());

            switch (socket.getBtStat()) {
                case 0:
                    port.setState(DeviceEnum.PortStatus.Available.name());
                    break;
                case 1:
                    port.setState(DeviceEnum.PortStatus.Charging.name());
                    break;
                case 6:
                case 7:
                    port.setState(DeviceEnum.PortStatus.Faulted.name());
                    break;
                default:
                    port.setState(DeviceEnum.PortStatus.Available.name());
                    break;
            }

            switch (socket.getBtStat()){
                case 2:
                    port.setReason(OrderStopReason.EB.TIMEEND.name());
                    port.setStopReason(OrderStopReason.EB.TIMEEND.getValue());
                    break;
                case 3:
                    port.setReason(OrderStopReason.EB.FULLEND.name());
                    port.setStopReason(OrderStopReason.EB.FULLEND.getValue());
                    break;
                case 4:
                    port.setReason(OrderStopReason.EB.BREAK.name());
                    port.setStopReason(OrderStopReason.EB.BREAK.getValue());
                    break;
                case 5:
                    port.setReason(OrderStopReason.EB.OVERLOAD_CANCEL.name());
                    port.setStopReason(OrderStopReason.EB.OVERLOAD_CANCEL.getValue());
                    break;
                case 6:
                    port.setReason(OrderStopReason.EB.ERROR.name());
                    port.setStopReason(OrderStopReason.EB.ERROR.getValue());
                    break;
                case 7:
                    port.setReason(OrderStopReason.EB.HIGH_TEMP.name());
                    port.setStopReason(OrderStopReason.EB.HIGH_TEMP.getValue());
                    break;

            }

            ports.add(port);
        }
        return ports;
    }

    private EBExceptionEnum getException(Integer errorCode){
        EBExceptionEnum reason = EBExceptionEnum.NoError;
        switch (errorCode){
            case 1:
                reason = EBExceptionEnum.HighTemp;
                break;
            case 2:
                reason = EBExceptionEnum.LowPressure;
                break;
            case 3:
                reason = EBExceptionEnum.OverPressure;
                break;
            case 4:
                reason = EBExceptionEnum.OverPower;
                break;
            case 5:
                reason = EBExceptionEnum.RelayBind;
                break;
            case 6:
                reason = EBExceptionEnum.FuseWire;
                break;
            case 7:
                reason = EBExceptionEnum.BusFault;
                break;
            case 8:
                reason = EBExceptionEnum.NetworkFault;
                break;
            case 9:
                reason = EBExceptionEnum.N4GFault;
                break;
            case 10:
                reason = EBExceptionEnum.StorageFault;
                break;
            case 11:
                reason = EBExceptionEnum.CoilFault;
                break;
        }
        return reason;
    }
}
